<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Easyui Datagrid：表格里的编辑单元格上下左右移动</title>
    <script src="js/jquery.min.js"></script>
    <script src="js/jquery.easyui.min.js"></script>
    <link rel="stylesheet" type="text/css" href="js/easyui.css">
    <link>
</head>
<body>
<div id="materialTB">
    <ul class="list-inline" style="padding-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; margin-bottom: 0px;">
    <li style="padding-left: 0px;padding-right: 0px;">
        <label style="color: red">
            <span style="font-size: 6px;">Easyui Datagrid：表格里的编辑单元格上下左右移动(类似Excel键盘移动)；[↑↓←→移动] [ctrl+↑↓复制] [enter向下移动]</span>
        </label>
    </li>
    </ul>
</div>
<table id="material" name="material" class="easyui-datagrid" data-options="toolbar:'#materialTB'" fit="true">
</table>
<script>
    $(function(){
        //虚拟3行数据
        var mydata = 
            [{materialNumber:1,repeatCode:'R',materialDescriptionEn:'Brown plastic(outsole)',materialDescriptionChs:'棕色塑料 大底'},
            {materialNumber:2,repeatCode:'RR',materialDescriptionEn:'Clear plastic(shoelace head)',materialDescriptionChs:'透明塑料 鞋带头'},
            {materialNumber:3,repeatCode:'',materialDescriptionEn:'Black foam(interlining of tongue and upper',materialDescriptionChs:'黑色泡沫 鞋舌和鞋面的夹层'}];

        $('#material').datagrid({
            width:'100%',
            singleSelect: true,
            data:mydata,
            selectOnCheck:false,
            checkOnSelect:false,
            rownumbers:true,
            remoteSort: false,
            columns: [[
                {field: 'ck', checkbox:'true',halign: 'center'},
                {field: 'materialNumber', title:'材料序号',width:80, align: 'center',editor: 'numberbox',halign: 'center',sortable: true},
                {field: 'repeatCode', title: '重测编号', width: '100',halign: 'center',sortable: true,editor:'textarea'},
                {field: 'materialDescriptionEn', title: '材料描述(英)', width: '300',halign: 'center',sortable: true,editor:'textarea'},
                {field: 'materialDescriptionChs', title: '材料描述(中)', width: '300',halign: 'center',sortable: true,editor:'textarea'},
            ]],
            onClickCell(index, field, value) {
                //延迟加载0.1毫秒，不然点击后不能马上选中，即选中当前行和点击行的索引不一致，ClickCell触发完成后才能选中点击行，不然onSelect会执行2次
                setTimeout(function () {
                    if (isEndEditing($('#material'))) {
                        //上下左右移动，上下复制
                        myCellUpDownLeftRightEdit($('#material'),index,field,'Click',['ck'],'DisableUse',undefined,true,undefined);
                        _editIndex = index;
                    }
                },0.1);
            },
        });



        //**********启动datagrid编辑所需的代码*************//
        var _editIndex = undefined;
        function isEndEditing(dg){
            if (_editIndex == undefined){return true}
            if (dg.datagrid('validateRow', _editIndex)){
                dg.datagrid('endEdit', _editIndex);
                _editIndex = undefined;
                return true;
            } else {
                return false;
            }
        }
        //**********启动datagrid编辑所需的代码*************//


        //**********启动datagrid编辑所需,扩展方法的代码*************//
        $.extend($.fn.datagrid.methods, {
            editCell: function(jq,param,_isMaterialOrPrmsubId){
                //20190424添加，用于步骤2，项目和材料表选中时，自定义的isMaterial isPrmsub等的选中丢失问题
                var tempObj = document.getElementById(_isMaterialOrPrmsubId);
                var tempBol = undefined;
                if(tempObj != undefined){
                    tempBol = tempObj.checked;
                }

                var result = jq.each(function(){
                    var opts = $(this).datagrid('options');
                    var fields = $(this).datagrid('getColumnFields',true).concat($(this).datagrid('getColumnFields'));
                    for(var i=0; i<fields.length; i++){
                        var col = $(this).datagrid('getColumnOption', fields[i]);
                        col.editor1 = col.editor;
                        if (fields[i] != param.field){
                            col.editor = null;
                        }
                    }
                    $(this).datagrid('beginEdit', param.index);
                    for(var i=0; i<fields.length; i++){
                        var col = $(this).datagrid('getColumnOption', fields[i]);
                        col.editor = col.editor1;
                    }
                });

                //20190424添加，用于步骤2，项目和材料表选中时，自定义的isMaterial isPrmsub等的选中丢失问题
                tempObj = document.getElementById(_isMaterialOrPrmsubId);
                if(tempObj != undefined){
                    tempObj.checked = tempBol;
                }

                return result;
            }
        });
        //**********启动datagrid编辑所需,扩展方法的代码*************//



        /*******************20181028,datagrid,单元格Cell上下左右移动，上下复制****************************************/
            //20181028,实现datagrid的Cell的上下左右移动，上下复制等
            //移动：ctrl+↑↓←→移动(tab 和→一样)
            //复制：ctrl+alt+↑↓上下复制
            //注意：datebox、datetimebox、combotree未完全实现，存在Bug，故未启用,但可以控制跳过
            //其中：textbox、textareabox、combobox、numberbox皆可使用
            //vialdebox、checkbox不进行实现
            //dg: datagrid的表对象
            //index: 当前启用编辑的行索引
            //field：当前启用编辑的字段名
            //direction：按键的操作方向7种：Click、UP、UPCOY、DOWN、DOWNCOPY、LEFT、RIGHT
            //columnsEdit和isColumnUse配合使用：isColumnUse有2种状态：DisableUse、EableUse
            //      DisableUse时，columnsEdit的数组存储不能编辑的字段名字
            //      EableUse时，columnsEdit的数组存储能编辑的字段名字
            //oldEditor：用于复制，抓取上一个编辑的内容，复制给新的Editor

        function myCellUpDownLeftRightEdit(dg, index, field, direction, columnsEdit, isColumnUse, oldEditor, isEnterDown,_isMaterialOrPrmsubId) {
            //20201126，通过新增一个隐形的input名为“_DATAGRID_NUMBERBOX”的控件，延迟1毫秒的焦点定位，然后下调一个单元格，不然Numberbox的左右移动失效
            var _DATAGRID_NUMBERBOX=document.getElementById('_DATAGRID_NUMBERBOX');
            if(_DATAGRID_NUMBERBOX==undefined && dg != undefined && dg.length > 0){
                var dgDom = dg[0];
                if(dgDom != undefined && dgDom.parentNode != undefined){
                    var _DATAGRID_NUMBERBOX_input = document.createElement("input");
                    _DATAGRID_NUMBERBOX_input.setAttribute('type', 'text');
                    _DATAGRID_NUMBERBOX_input.id="_DATAGRID_NUMBERBOX";
                    _DATAGRID_NUMBERBOX_input.style="padding-left: 0px;padding-right: 0px;border-right-width: 0px;border-top-width: 0px;border-left-width: 0px;border-bottom-width: 0px;width: 1px;padding-bottom: 0px;height: 1px;";
                    dgDom.parentNode.appendChild(_DATAGRID_NUMBERBOX_input);
                }
            }
            var nextIndex = index;
            if (direction != undefined && direction != 'Click') {
                if (direction == 'UP' || direction == 'DOWN' || direction == 'UPCOPY' || direction == 'DOWNCOPY') {
                    nextIndex = (direction == 'UP' || direction == 'UPCOPY') ? index - 1 : index + 1;
                    var dgCount = dg.datagrid('getData').rows.length;
                    if (dgCount == 1) {
                        return;
                    }

                    if ((direction == 'UP' || direction == 'UPCOPY') && nextIndex < 0) {
                        nextIndex = (dgCount - 1);
                    }

                    if ((direction == 'DOWN' || direction == 'DOWNCOPY') && (nextIndex - 1) == (dgCount - 1)) {
                        nextIndex = 0;
                    }
                }
                if (direction == 'LEFT' || direction == 'RIGHT') {
                    var columnsAll = dg.datagrid('getColumnFields');
                    var columnsCount = columnsAll.length;
                    var isFindColumn = false;
                    if (columnsCount > 0) {
                        if (direction == 'RIGHT') {
                            for (var i = 0; i < columnsCount; i++) {
                                var columnField = columnsAll[i];
                                // console.log(columnField);
                                if (isColumnUse == 'DisableUse' && columnsEdit.indexOf(columnField) > -1) {
                                    if (i == (columnsCount - 1)) {
                                        i = -1;
                                    }
                                    continue;
                                }
                                if (isColumnUse == 'EableUse' && columnsEdit.indexOf(columnField) == -1) {
                                    if (i == (columnsCount - 1)) {
                                        i = -1;
                                    }
                                    continue;
                                }
                                //判断列是否隐藏
                                if(dg.datagrid('getPanel').find('tr.datagrid-header-row').children('td[field="' + columnField + '"]')[0].style.display=='none'){
                                    if (i == (columnsCount - 1)) {
                                        i = -1;
                                    }
                                    continue;
                                }
                                if (columnField == field) {
                                    if (i == (columnsCount - 1)) {
                                        i = -1;
                                    }
                                    isFindColumn = true;
                                    continue;
                                }
                                if (isFindColumn) {
                                    field = columnField;
                                    break;
                                }
                            }
                        }

                        if (direction == 'LEFT') {
                            for (var i = columnsCount - 1; i >= 0; i--) {
                                var columnField = columnsAll[i];
                                if (isColumnUse == 'DisableUse' && columnsEdit.indexOf(columnField) > -1) {
                                    if (i == 0) {
                                        i = columnsCount;
                                    }
                                    continue;
                                }
                                if (isColumnUse == 'EableUse' && columnsEdit.indexOf(columnField) == -1) {
                                    if (i == 0) {
                                        i = columnsCount;
                                    }
                                    continue;
                                }
                                //判断列是否隐藏
                                if(dg.datagrid('getPanel').find('tr.datagrid-header-row').children('td[field="' + columnField + '"]')[0].style.display=='none'){
                                    if (i == (columnsCount - 1)) {
                                        i = -1;
                                    }
                                    continue;
                                }
                                if (columnField == field) {
                                    if (i == 0) {
                                        i = columnsCount;
                                    }
                                    isFindColumn = true;
                                    continue;
                                }
                                if (isFindColumn) {
                                    field = columnField;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            dg.datagrid('editCell', {index: nextIndex, field: field, _isMaterialOrPrmsubId:_isMaterialOrPrmsubId});
            var ed = dg.datagrid('getEditor', {index: nextIndex, field: field});

            //点击触发编辑，不能editor的
            if (direction == 'Click' && (ed == null || ed == undefined)) {
                return;
            }
            if (ed != null && ed != undefined) {
                dg.datagrid('selectRow', nextIndex);
                var _type = ed.type;
                if (_type == 'text' || _type == 'textarea') {
                    //************************新加，20200304启用以下代码，单元格字符间的移动，最前最后时，自动上移或下移一个单元格************************//
                    if(direction == 'LEFT'){
                        //光标在下一个单元格的第一个字符前，按键向左后，光标停留当前单元格的最后一个字符后面，必须得延迟加载，不然在最后一个字符的前面
                        setTimeout(function () {
                            if(ed.target[0] != undefined){
                                if($(ed.target).val() != undefined){
                                    ed.target[0].setSelectionRange($(ed.target).val().length,$(ed.target).val().length);
                                }
                            }
                        },0.01);
                    }
                    if(direction == 'RIGHT'){
                        //光标在上一个单元格的最后一个字符后，按键向右后，光标停留当前单元格的第一个字符前面，必须得延迟加载，不然在第一个字符后面
                        setTimeout(function () {
                            if(ed.target[0] != undefined){
                                ed.target[0].setSelectionRange(0,0);
                            }
                        },0.01);
                    }
                    //************************新加，20200304启用以下代码，单元格字符间的移动，最前最后时，自动上移或下移一个单元格************************//


                    $(ed.target).focus();
                    if ((direction == 'UPCOPY' || direction == 'DOWNCOPY') && oldEditor != undefined) {
                        $(ed.target).val($(oldEditor.target).val());
                    }

                    $(ed.target).bind('blur', function () {
                        dg.datagrid('endEdit', nextIndex);
                    });

                    myCell_KeyDown($(ed.target), dg, nextIndex, field, ed, columnsEdit, isColumnUse, isEnterDown);
                }
                if (_type == 'numberbox') {
                    $(ed.target).numberbox().next('span').find('input').focus();
                    if ((direction == 'UPCOPY' || direction == 'DOWNCOPY') && oldEditor != undefined) {
                        $(ed.target).numberbox('setValue', $(oldEditor.target).val());
                    }

                    $(ed.target).next('.numberbox').find('input').blur(function () {
                        dg.datagrid('endEdit', nextIndex);
                    });
                    myCell_KeyDown($(ed.target).next('.numberbox').find('input'), dg, nextIndex, field, ed, columnsEdit, isColumnUse, isEnterDown);
                }
                if (_type == 'datebox' || _type == 'datetimebox') {
                    $(ed.target).datetimebox('textbox').focus();

                    // console.log('1111');
                    //20210108 start
                    $(ed.target).focus();
                    if ((direction == 'UPCOPY' || direction == 'DOWNCOPY') && oldEditor != undefined) {
                        $(ed.target).val($(oldEditor.target).val());
                    }

                    var isEnd;
                    $(ed.target).datetimebox('textbox').bind('blur', function () {
                        console.log('text||11::blur');
                        if(isEnd == 1){
                            dg.datagrid('endEdit', nextIndex);
                        }
                    });
                    myCell_KeyDown($(ed.target).datetimebox('textbox'), dg, nextIndex, field, ed, columnsEdit, isColumnUse, isEnterDown);
                }
                if (_type == 'combobox') {
                    if ((direction == 'UPCOPY' || direction == 'DOWNCOPY') && oldEditor != undefined) {
                        $(ed.target).combobox('setValue', $(oldEditor.target).combobox('getValue'));
                    }
                    if (direction == 'Click') {
                        $(ed.target).focus();
                        $(ed.target).bind('blur', function () {
                            dg.datagrid('endEdit', nextIndex);
                        });
                    } else {
                        $(ed.target).next('.combo').find('input').focus();
                    }
                    $(ed.target).next('.combo').find('input').blur(function () {
                        dg.datagrid('endEdit', nextIndex);
                    });
                    myCell_KeyDown($(ed.target).next('.combo').find('input'), dg, nextIndex, field, ed, columnsEdit, isColumnUse, isEnterDown);
                }
                if (_type == 'combotree') {
                    if (direction == 'Click') {
                        $(ed.target).focus();
                        $(ed.target).bind('blur', function () {
                            dg.datagrid('endEdit', nextIndex);
                        });
                    } else {
                        $(ed.target).next('.combo').find('input').focus();
                    }
                    $(ed.target).next('.combo').find('input').blur(function () {
                        dg.datagrid('endEdit', nextIndex);
                    });
                    myCell_KeyDown($(ed.target).next('.combo').find('input'), dg, nextIndex, field, ed, columnsEdit, isColumnUse, isEnterDown);
                }
            }
        }



        //注册每个新的Editor的监听事件
        function myCell_KeyDown(obj, dg, index, field, ed, columnsEdit, isColumnUse, isEnterDown) {
            var _type = ed.type;
            //datetimebox和datebox的特殊性
            if (_type == 'datebox' || _type == 'datetimebox') {
                _isLastCellDatetime_EditIndex = index;
            }
            //datetimebox和datebox的特殊性
            obj.bind('keydown', function (e) {
                if (_type == 'datebox' || _type == 'datetimebox') {
                    _isLastCellDatetime_EditIndex = index;
                    $(ed.target).datetimebox('textbox').blur(function () {
                        // console.log('datetimebox:$(ed.target).textbox:blur');
                        dg.datagrid('endEdit', index);
                    });
                }

                var ctrlKey = e.ctrlKey;
                //ctrl+alt+up 20181229 改为ctrl+up
                if (ctrlKey && e.keyCode == 38) {
                    // console.log("ctrl+alt+up");
                    //取消浏览器自带的快捷键事件
                    if (ctrlKey && window.event.keyCode == 38) {
                        window.event.preventDefault();
                        window.event.cancelBubble = true;
                    }
                    myCellUpDownLeftRightEdit(dg, index, field, 'UPCOPY', columnsEdit, isColumnUse, ed, isEnterDown);
                }
                //ctrl+alt+down 20181229 改为ctrl+down
                if (ctrlKey && e.keyCode == 40) {
                    // console.log("ctrl+down");
                    //取消浏览器自带的快捷键事件
                    if (ctrlKey && window.event.keyCode == 40) {
                        window.event.preventDefault();
                        window.event.cancelBubble = true;
                    }
                    myCellUpDownLeftRightEdit(dg, index, field, 'DOWNCOPY', columnsEdit, isColumnUse, ed, isEnterDown);
                }

                //ctrl+up 20181229 改为up
                if (!ctrlKey && e.keyCode == 38) {
                    // console.log("ctrl+up");
                    //取消浏览器自带的快捷键事件
                    if (window.event.keyCode == 38) {
                        window.event.preventDefault();
                        window.event.cancelBubble = true;
                    }
                    myCellUpDownLeftRightEdit(dg, index, field, 'UP', columnsEdit, isColumnUse, ed, isEnterDown);
                }

                //20190103，添加按回车事件，换行，和向下一样的效果
                if(isEnterDown==true){
                    //ctrl+down 20181229 改为down，或者20190103，回车事件换行
                    if ((!ctrlKey && e.keyCode == 40) || e.keyCode==13) {
                        //取消浏览器自带的快捷键事件
                        if (window.event.keyCode == 13) {
                            window.event.preventDefault();
                            window.event.cancelBubble = true;
                        }
                        myCellUpDownLeftRightEdit(dg, index, field, 'DOWN', columnsEdit, isColumnUse, ed, isEnterDown);
                    }
                }else{
                    //ctrl+down 20181229 改为down
                    if (!ctrlKey && e.keyCode == 40) {
                        // console.log("down");
                        myCellUpDownLeftRightEdit(dg, index, field, 'DOWN', columnsEdit, isColumnUse, ed, isEnterDown);
                    }
                }

                //ctrl+left 20181229 改为left
                if (e.keyCode == 37) {
                    //************************20200304启用以下代码，单元格字符间的移动，最前最后时，自动上移或下移一个单元格************************//
                    var contextCurrent = $(this).context;
                    var selectionStartPosition;
                    var selectionEndPosition;
                    if(contextCurrent != undefined){
                        selectionStartPosition = contextCurrent.selectionStart;//选中区域左边界
                        selectionEndPosition = contextCurrent.selectionEnd;//选中区域右边界
                    }
                    //如果光标在单元格内的字符的最前面，就进行上一个单元格的操作，不然就是单元格内容字符间的移动
                    if((selectionStartPosition==0 && selectionEndPosition==0) || contextCurrent==undefined){
                        //20201126，通过新增一个隐形的input名为“_DATAGRID_NUMBERBOX”的控件，延迟1毫秒的焦点定位，然后下调一个单元格，不然Numberbox的左右移动失效
                        document.getElementById('_DATAGRID_NUMBERBOX').focus();
                        // document.body.focus();
                        setTimeout(function(){
                            //左右移动要取消离开事件
                            myCell_Blur(ed);
                            dg.datagrid('endEdit', index);
                            myCellUpDownLeftRightEdit(dg, index, field, 'LEFT', columnsEdit, isColumnUse, ed, isEnterDown);
                        },1);
                    }
                    //************************20200304启用以下代码，单元格字符间的移动，最前最后时，自动上移或下移一个单元格************************//
                }
                //ctrl+right or tab 20181229 改为right or tab
                if (e.keyCode == 39 || e.keyCode == 9) {
                    //************************20200304启用以下代码，单元格字符间的移动，最前最后时，自动上移或下移一个单元格************************//
                    if(e.keyCode == 39){
                        var contextCurrent = $(this).context;
                        var selectionStartPosition;
                        var selectionEndPosition;
                        if(contextCurrent != undefined){
                            selectionStartPosition = contextCurrent.selectionStart;//选中区域左边界
                            selectionEndPosition = contextCurrent.selectionEnd;//选中区域右边界
                        }
                        //如果光标在单元格内的字符的后面前面，就进行下一个单元格的操作，不然就是单元格内容字符间的移动
                        if($(this).val()==undefined || $(this).val()=='' || (($(this).val()==undefined?true:(selectionStartPosition == $(this).val().length))) || contextCurrent==undefined){
                            //20201126，通过新增一个隐形的input名为“_DATAGRID_NUMBERBOX”的控件，延迟1毫秒的焦点定位，然后下调一个单元格，不然Numberbox的左右移动失效
                            document.getElementById('_DATAGRID_NUMBERBOX').focus();
                            // document.body.focus();
                            setTimeout(function(){
                                //左右移动要取消离开事件
                                myCell_Blur(ed);
                                dg.datagrid('endEdit', index);
                                myCellUpDownLeftRightEdit(dg, index, field, 'RIGHT', columnsEdit, isColumnUse, ed, isEnterDown);
                            },1);
                        }
                    }
                    if(e.keyCode == 9){
                        //20201126，通过新增一个隐形的input名为“_DATAGRID_NUMBERBOX”的控件，延迟1毫秒的焦点定位，然后下调一个单元格，不然Numberbox的左右移动失效
                        document.getElementById('_DATAGRID_NUMBERBOX').focus();
                        // document.body.focus();
                        setTimeout(function(){
                            //左右移动要取消离开事件
                            myCell_Blur(ed);
                            dg.datagrid('endEdit', index);
                            myCellUpDownLeftRightEdit(dg, index, field, 'RIGHT', columnsEdit, isColumnUse, ed, isEnterDown);
                        },1);
                    }
                    //************************20200304启用以下代码，单元格字符间的移动，最前最后时，自动上移或下移一个单元格************************//
                }
            });
        }


        //左右移动的离开事件
        function myCell_Blur(ed) {
            var _type = ed.type;
            // console.log('Blur:type::' + _type);
            if (_type == 'text' || _type == 'textarea') {
                $(ed.target).unbind('blur');
            }
            if (_type == 'numberbox') {
                $(ed.target).next('.numberbox').find('input').unbind('blur');
            }
            if (_type == 'datebox' || _type == 'datetimebox') {
                // $(ed.target).unbind('blur');
            }
            if (_type == 'combobox') {
                $(ed.target).unbind('blur');
                $(ed.target).next('.combo').find('input').unbind('blur');
            }
            if (_type == 'combotree') {
                $(ed.target).combotree().next('span').find('input').unbind('blur');
            }
        }

        /*******************20181028,datagrid,单元格Cell上下左右移动，上下复制****************************************/


    });

</script>
</body>

</html>